﻿///////////////////////////////////////////////////////////////////////////////
//
// This file is part of the BassDrop project
//   https://code.google.com/p/bass-drop/
//
//
// Creator:	Gergely Krajcsovszki (krajcsovszkig@hotmail.com)
// Date:	2013-08-05 13:30
//
//
// Copyright © 2013-2014. Gergely Krajcsovszki
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BassDrop.Core.Common.Cloud;
using BassDrop.Core.Common.Exceptions.Cloud;
using BassDrop.Core.Common.Localization;
using BassDrop.Core.Common.Options;
using BassDrop.Core.Common.Options.Items;
using BassDrop.Core.Common.Services;
using BassDrop.Plugins.Dropbox.Localization;
using DropNetRT;
using DropNetRT.Models;

namespace BassDrop.Plugins.Dropbox
{
	/// <summary>
	/// Dropbox plugin options
	/// </summary>
	public class OptionsSet : CloudOptionsSetBase
	{
		private readonly OptionsItemBase m_connectAccountActionItem;
		private bool m_connecting = false;

		/// <summary>
		/// Constructs a new options set
		/// </summary>
		/// <param name="plugin">the plugin whose options these are</param>
		/// <param name="instance">the plugin instance whose options these are or null if they're for the plugin</param>
		public OptionsSet( BassDropPlugin plugin, Connection instance )
			: base( plugin, instance )
		{
			m_connectAccountActionItem = new ActionOptionsItem(
				ConnectAccount,
				PluginStrings.LabelConnectAccount,
				PluginStrings.DescriptionConnectAccount );

			OptionList.Add( m_connectAccountActionItem );

			UpdateAccountInfoDisplay();

			PluginStrings.StaticPropertyChanged += Strings_StaticPropertyChanged;
			Strings.StaticPropertyChanged += Strings_StaticPropertyChanged;
		}

		#region Methods

		/// <summary>
		/// Displays a dialog that lets the user connect BassDrop to their Dropbox account
		/// </summary>
		private async Task ConnectAccount()
		{
			try
			{
				m_connecting = true;
				UpdateAccountInfoDisplay();

				var requestToken = await ((Connection) Instance).Client.GetRequestToken();
				string url = ((Connection) Instance).Client.BuildAuthorizeUrl( requestToken );

				ServiceCenter.WebBrowser.Open( url );

				var accessToken = await ((Connection) Instance).Client.GetAccessToken();

				if ( accessToken.Token == null || accessToken.Secret == null )
				{
					await SetUserLogin( null );
					throw new CloudUnauthorizedException( PluginStrings.ExceptionFailedToConnectAccount, null );
				}
				else
				{
					await SetUserLogin( accessToken );
				}
			}
			catch ( Exception e )
			{
				throw e.GetCloudException();
			}
			finally
			{
				m_connecting = false;
				UpdateAccountInfoDisplay();
			}
		}

		/// <summary>
		/// Called when the owner instance of this options set is deleted
		/// </summary>
		protected override void InstanceRemoved()
		{
			ServiceCenter.UserInterface.ShowMessageBox(
				PluginStrings.DescriptionRemoveApplicationFromDropbox,
				PluginStrings.LabelRemoveApplicationFromDropbox,
				PortableMessageBoxButton.OK,
				PortableMessageBoxImage.Information );
		}

		#endregion Methods

		#region Accessors of option values

		private UserLogin m_userLogin;

		/// <summary>
		/// The login information of the current user
		/// </summary>
		public UserLogin UserLogin
		{
			get
			{
				return m_userLogin;
			}
		}

		/// <summary>
		/// Sets the value of the UserLogin property. Asynchronously updates the AccountInfo property
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
		private async Task SetUserLogin( UserLogin value )
		{
			if ( m_userLogin != value )
			{
				m_userLogin = value;

				OnPropertyChanged( new PropertyChangedEventArgs( "UserLogin" ) );

				await UpdateAccountInfo();
			}
		}

		private DropNetRT.Models.AccountInfo m_accountInfo;

		/// <summary>
		/// Information about the connected account or null if nothing is connected
		/// </summary>
		public DropNetRT.Models.AccountInfo AccountInfo
		{
			get
			{
				return m_accountInfo;
			}
			private set
			{
				if ( value != m_accountInfo )
				{
					m_accountInfo = value;

					OnPropertyChanged( new PropertyChangedEventArgs( "AccountInfo" ) );

					UpdateAccountInfoDisplay();
				}
			}
		}

		/// <summary>
		/// Updates the AccountInfo property from the currently connected account
		/// </summary>
		private async Task UpdateAccountInfo()
		{
			if ( ((Connection) Instance).IsAvailable )
			{
				try
				{
					AccountInfo = await ((Connection) Instance).Client.AccountInfo();
				}
				catch ( Exception )
				{
					AccountInfo = null;
					// TODO: hibajelzés a gombon, automata frissítés később
					// pl. lehetne egy függvénye az optionsseteknek, amit visiblechange esetén meghív az UI, erre újratesztelni
				}
			}
			else
			{
				AccountInfo = null;
			}
		}

		/// <summary>
		/// Update the display of the account info
		/// </summary>
		private void UpdateAccountInfoDisplay()
		{
			if ( m_connecting )
			{
				m_connectAccountActionItem.Name = PluginStrings.LabelConnectingToCloud;
				m_connectAccountActionItem.Enabled = false;
			}
			else if ( AccountInfo != null )
			{
				m_connectAccountActionItem.Name = string.Format( "{0} {1} ({2})", Strings.LabelCloudSettingsAccount, AccountInfo.DisplayName, AccountInfo.Email );
				m_connectAccountActionItem.Enabled = false;
			}
			else
			{
				m_connectAccountActionItem.Name = PluginStrings.LabelConnectAccount;
				m_connectAccountActionItem.Enabled = true;
			}
		}

		#endregion Accessors of option values

		/// <summary>
		/// Returns the options of this item
		/// </summary>
		/// <returns></returns>
		protected override IDictionary<string, string> GetCustomOptions()
		{
			IDictionary<string, string> ret = new Dictionary<string, string>();

			if ( UserLogin != null )
			{
				ret.Add( "UserLogin_Secret", ServiceCenter.EncryptLocal( UserLogin.Secret ) );
				ret.Add( "UserLogin_Token", ServiceCenter.EncryptLocal( UserLogin.Token ) );
			}
			else
			{
				ret.Add( "UserLogin_Secret", null );
				ret.Add( "UserLogin_Token", null );
			}

			return ret;
		}

		/// <summary>
		/// Sets the options of this item
		/// </summary>
		/// <returns></returns>
		protected override async Task SetCustomOptions( IDictionary<string, string> dictionary )
		{
			UserLogin userLogin = new UserLogin();

			if ( dictionary.ContainsKey( "UserLogin_Secret" ) )
			{
				userLogin.Secret = dictionary["UserLogin_Secret"];

				if ( userLogin.Secret == string.Empty )
				{
					userLogin.Secret = null;
				}
				else
				{
					userLogin.Secret = ServiceCenter.DecryptLocal( userLogin.Secret );
				}
			}

			if ( dictionary.ContainsKey( "UserLogin_Token" ) )
			{
				userLogin.Token = dictionary["UserLogin_Token"];

				if ( userLogin.Token == string.Empty )
				{
					userLogin.Token = null;
				}
				else
				{
					userLogin.Token = ServiceCenter.DecryptLocal( userLogin.Token );
				}
			}

			if ( userLogin.Secret != null && userLogin.Token != null )
			{
				await SetUserLogin( userLogin );
			}
			else
			{
				await SetUserLogin( null );
			}
		}

		private void Strings_StaticPropertyChanged( object sender, PropertyChangedEventArgs e )
		{
			if ( e.PropertyName == "LabelConnectAccount"
				|| e.PropertyName == "LabelCloudSettingsAccount"
				|| e.PropertyName == "LabelConnectingToCloud" )
			{
				UpdateAccountInfoDisplay();
			}
			else if ( e.PropertyName == "DescriptionConnectAccount" )
			{
				m_connectAccountActionItem.Description = PluginStrings.DescriptionConnectAccount;
			}
		}
	}
}
